package czopyk.model.structure;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import pl.czopyk.colt.math.DistributionWrapper;
import czopyk.model.cost.DistributionId;
import czopyk.model.cost.DistributionManager;

public class Modulator {

	protected static final Logger log = Logger.getLogger("Modulator");

	private LoadGenerator loadGenerator;
	private Simulator simulator;
	private DistributionManager distributionManager;
	
	// dystrybucje
	private Map<String, Center> centers = new HashMap<String, Center>();

	public void addUser() {
		loadGenerator.addUser();
	}

	/**
	 * @return false, jeśli nie da się usunąć
	 */
	public boolean removeUser() {
		if (!loadGenerator.canRemoveUser()) {
			log.warn("Cannot remove user");
			return false;
		}
		log.info("Removing user...");
		loadGenerator.removeUser();
		return true;
	}
	
	public void setUsersCount(int uc) {
		if (uc <= 0) {
			log.warn("Trying to set users count to '" + uc + "'. Setting to 1 (min).");
			uc = 1;
		}
		if (uc >= 101) {
			log.warn("Trying to set users count to '" + uc + "'. Setting to 100 (max).");
			uc = 100;
		}

		log.info("Setting users count to: " + uc);
		int c = 0;
		while ((c = loadGenerator.getUsersCount()) != uc) {
			if (c < uc) {
				addUser();
			} else {
				removeUser();
			}
		}
	}

	public void changeDistributionOffset(DistributionId id, double offset) {
		DistributionWrapper dw = distributionManager.getDistributionById(id);
		if (dw == null) {
			throw new IllegalArgumentException("Distribution '" + id + "' not found.");
		}
		dw.setOffset(offset);
	}
	
	public void changeDistributionWithParams(DistributionId id, Double[] params) {
		DistributionWrapper dw = distributionManager.getDistributionById(id);
		if (dw == null) {
			throw new IllegalArgumentException("Distribution '" + id + "' not found.");
		}
		dw.setState(params);
	}

	protected void validate(Double[] params, Class<?> c, String args) {
		if (params.length != args.split(" ").length) {
			throw new IllegalArgumentException(c.getSimpleName()
					+ " distribution requires exactly " + args.split(" ").length
					+ " argument(s): " + args + ".");
		}
	}

	public void setChannelsInCenter(String centerName, int Channels) {
		Center c = getCenter(centerName);
		log.info("Setting channels in center '" + centerName + "' to: " + Channels);
		c.setChannels(Channels);
	}

	public int addChannelToCenter(String centerName) {
		Center c = getCenter(centerName);
		log.info("Increasing center '" + centerName + "' channels number to "
				+ c.getChannels());
		c.setChannels(c.getChannels() + 1);
		return c.getChannels();
	}

	public int removeChannelFromCenter(String centerName) {
		Center c = getCenter(centerName);
		log.info("Decreasing center '" + centerName + "' channels number to "
				+ c.getChannels());
		c.setChannels(c.getChannels() - 1);
		return c.getChannels();
	}

	private Center getCenter(String name) {
		// późna inicjalizacja
		if (centers.isEmpty()) {
			initCentersMap();
		}
		Center c = centers.get(name);
		if (c == null) {
			throw new IllegalArgumentException("Center named '" + name
					+ "' does not exists.");
		}
		return c;
	}

	private void initCentersMap() {
		centers.clear();
		List<Center> l = new LinkedList<Center>();
		l.add(simulator.getGate());
		while (!l.isEmpty()) {
			Center c = l.remove(0);
			centers.put(c.getName(), c);
			l.addAll(c.getAllNext());
		}
	}

	public void setLoadGenerator(LoadGenerator loadGenerator) {
		this.loadGenerator = loadGenerator;
	}

	public void setSimulator(Simulator simulator) {
		this.simulator = simulator;
	}

	
	public void setHitRateInCenter(String center, Double value) {
		Center c = getCenter(center);
		if (!(c instanceof Cache)) {
			throw new IllegalArgumentException("Center '" + center
					+ "' is not a cache. Cannot change hit rate.");
		}
		if (value > 1.0) {
			log.warn("Trying to set hitRate of center '" + center + "' to " + value
					+ ". Setting to 1.0.");
			value = 1.0;
		}
		if (value < 0.0) {
			log.warn("Trying to set hitRate of center '" + center + "' to " + value
					+ ". Setting to 0.0.");
			value = 0.0;
		}

		Cache cache = (Cache) c;
		log.info("Setting hitRate of center '" + center + "' to " + value);
		cache.setHitRate(value);
	}

	/*
	public void setInterferenceFactor(String centerName, double value) {
		Center c = getCenter(centerName);
		log.info("Setting interefence factor in center '" + centerName + "' to "
				+ value);
		if (value > 1.0) {
			log.warn("Set interefence factor in center '" + centerName + "' to "
					+ value + ", which indicated processing slower than sequential.");
		}
		if (value < 0.0) {
			log.warn("Trying to set interefence factor in center '" + centerName + "' to "
					+ value + ". Setting to 0.0.");
			value = 0.0;
		}
		c.setInterferenceFactor(value);
	}
	*/
	
	public void setDistributionManager(DistributionManager distributionManager) {
		this.distributionManager = distributionManager;
	}
	
	
}
